import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Random;

public class PlainTextAttack {

	private String[][] m_mostCommon = {
			{ "be", "to", "of", "in", "on", "it", "he", "as", "do", "at", "by",
					"we", "or", "an", "my", "so", "up", "if", "go", "me", "no",
					"us" },
			{ "the", "and", "for", "not", "you", "but", "his", "say", "her",
					"she", "one", "all", "out", "who", "get", "can", "him",
					"see", "now", "its", "use", "two", "how", "our", "way",
					"new", "any", "day" },
			{ "that", "have", "with", "this", "form", "they", "will", "what",
					"when", "make", "like", "time", "just", "know", "take",
					"into", "year", "your", "good", "some", "them", "than",
					"then", "look", "only", "come", "over", "also", "back",
					"work", "well", "even", "want", "give", "most" },
			{ "would", "there", "their", "about", "which", "people", "could",
					"other", "think", "after", "first", "because", "these" } };
	// private String[][] m_mostCommon = { "I","a"};
	private char[][] m_keyArray = new char[52][2];
	private char[] m_letterArray = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
			'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
			'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
			'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
			'V', 'W', 'X', 'Y', 'Z' };
	static String m_letterString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	private int m_sizeOfInitialVector;
	private byte[][] m_blocks;
	private int m_tens;
	private byte[] m_initialVector;
	private byte[] m_encrypted;
	private byte[][][] m_decrypted;
	private int[] m_count = new int[52];
	private char[] m_order = { 'e', 'a', 'h', 'd', 'f', 'b', 'c', 'g' };
	private byte[][][] m_tryThis;
	private byte[] m_knownText;
	private byte[] m_knownChiper;
	private int m_countLetters = 0;
	private int[][] m_key;
	private String m_notFound = new String();
	private String m_found = new String();
	private String m_notUse = new String();
	

	public PlainTextAttack(byte[] text, byte[] key, byte[] initialVector,
			int initialVectorSize) throws IOException {
		m_sizeOfInitialVector = initialVectorSize;
		m_tens = 0;
		m_tens = 0;
		if (text.length % 8128 != 0)
			m_tens = 1;
		m_blocks = new byte[text.length / 8128 + m_tens][8128];// create room
																// for
																// the text by
																// blocks
		MoveToBlocks(text);// move to block of ten
		m_initialVector = initialVector;
		int i;
		for (i = 0; i < 52; i++) {// puts in the array
			m_keyArray[i][0] = m_letterString.charAt(i);
		}
	}

	public PlainTextAttack(byte[] text, byte[] initialVector, byte[] knownText,
			byte[] knownChiper, int initialVectorSize) throws IOException {
		m_sizeOfInitialVector = initialVectorSize;
		m_tens = 0;
		if (text.length % 8128 != 0)
			m_tens = 1;
		m_blocks = new byte[text.length / 8128 + m_tens][8128];// create room
																// for
																// the text by
																// blocks
		m_initialVector = initialVector;
		m_knownText = knownText;
		m_knownChiper = knownChiper;
		MoveToBlocks(text);// move to block of ten
		int i;

		for (i = 0; i < 52; i++) {// puts in the array
			m_keyArray[i][0] = m_letterString.charAt(i);
		}
		m_encrypted = new byte[8128];
		InitialDeciphering();
	}

	private void InitialDeciphering() {
		int i;
		for (i = 0; i < m_sizeOfInitialVector; i++) {
			m_encrypted[i] = (byte) (m_knownText[i] ^ m_initialVector[i]);
			// System.out.print(" " +m_encrypted[i]);
			if ((m_encrypted[i] > 'a' - 1 && m_encrypted[i] < 'z' + 1)
					|| (m_encrypted[i] > 'A' - 1 && m_encrypted[i] < 'Z' + 1)) {
				if (m_keyArray[m_letterString.indexOf(m_encrypted[i])][1] == '\0') {
					m_keyArray[m_letterString.indexOf(m_encrypted[i])][1] = (char) (m_knownChiper[i]);
					m_countLetters++;
				}
				// System.out.println(m_keyArray[(char)m_letterString.indexOf(m_encrypted[i])][0]
				// + " " + (char) ( m_knownChiper[i]));
			}
		}
		int count = 0;
		m_key = new int[52 - m_countLetters][52 - m_countLetters];
		for (i = 0; i < 52; i++) {
			System.out.println(m_keyArray[i][0] + " " + m_keyArray[i][1]);
			if (m_keyArray[i][1] == '\0') {
				m_notFound = m_notFound + m_keyArray[i][0];
				count++;
			} else {
				m_found = m_found + m_keyArray[i][1];
			}

		}
		notUse();
	}

	private void notUse() {
		int i;
		for(i = 0; i < 52;i++){
			if(m_found.indexOf(m_letterString.charAt(i)) ==  -1){
				m_notUse = m_notUse + m_letterString.charAt(i);
			}
		}
	}
	
	public void Encrypt(String key) throws IOException {
		EncryptionKey(key);// generate a key
		int i, j;
		m_encrypted = new byte[8128];
		for (i = 0; i < m_sizeOfInitialVector; i++)
			m_encrypted[i] = m_initialVector[i];
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < m_blocks[i].length; j++) {
				m_encrypted[j] = (byte) (m_blocks[i][j] ^ m_encrypted[j]);
				// does the Xor between the initial vector and the text
				if ((m_encrypted[j] > 'a' - 1 && m_encrypted[j] < 'z' + 1)
						|| (m_encrypted[j] > 'A' - 1 && m_encrypted[j] < 'Z' + 1))
					ChangeToEncryptedKey(j);
			}
			FileWriter file = new FileWriter("file.txt", true);
			file.write(new String(m_encrypted));
			file.close();
		}
	}

	// private void Compare(int length, int start, int place) {
	// int i, j;
	// boolean[] difArray = new boolean[length];
	// int dif = 3;
	// for (i = 0; i < m_mostCommon.length; i++) {
	// if (length == m_mostCommon[i].length()) {
	// dif = 0;
	// for (j = 0; j < length && dif < 3; j++) {
	// if (m_tryThis[place][start + j][0] != m_mostCommon[i]
	// .charAt(j)) {
	// dif++;
	// if (m_decrypted[place][start + j][1] != -1)
	// difArray[j] = true;
	// }
	// }
	// }
	// }
	// }
	//
	// private void CompareTwo(int length, int start, int place, int lines) {
	// if (lines < 2) {
	// int i, j;
	// boolean[] difArray = new boolean[length];
	// int dif = 3;
	// for (i = 0; i < m_mostCommon.length; i++) {
	// if (length == m_mostCommon[i].length()) {
	// dif = 0;
	// for (j = 0; j < length && dif < 2; j++) {
	// if (start + j < 10) {
	// if (m_tryThis[place - lines][start + j][0] != m_mostCommon[i]
	// .charAt(j)) {
	// dif++;
	// difArray[j] = true;
	// }
	// } else {
	// if (m_tryThis[place][10 - start + j][0] != m_mostCommon[i]
	// .charAt(j)) {
	// dif++;
	// difArray[j] = true;
	// }
	// }
	// }
	// }
	// }
	// }
	// }

	private void ChangeToEncryptedKey(int i) {
		m_encrypted[i] = (byte) m_keyArray[m_letterString
				.indexOf(m_encrypted[i])][1];
	}

	public void Decrypt() throws IOException {
		int i, j;
		m_encrypted = new byte[8128];
		m_decrypted = new byte[m_blocks.length][8128][2];
		m_tryThis = new byte[m_blocks.length][8128][2];
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < 8128; j++) {
				m_encrypted[j] = m_blocks[i][j];
			}
			for (j = 0; j < 8128; j++) {
				if ((m_encrypted[j] >= 'A' && m_encrypted[j] <= 'Z')
						|| (m_encrypted[j] >= 'a' && m_encrypted[j] <= 'z')) {
					if (m_keyArray[m_letterString.indexOf(m_encrypted[j])][1] == '\0') {
						m_decrypted[i][j][1] = m_initialVector[j];
						m_decrypted[i][j][0] = m_encrypted[j];
						m_count[m_encrypted[j] - 'a']++;
					} else {
						m_decrypted[i][j][1] = -1;
						m_decrypted[i][j][0] = (byte) (m_keyArray[m_letterString
								.indexOf(m_encrypted[j])][1] ^ m_initialVector[j]);
						// m_count[m_encrypted[j] - 'a']++;

					}
				} else {
					m_decrypted[i][j][1] = -1;
					m_decrypted[i][j][0] = (byte) (m_initialVector[j] ^ m_encrypted[j]);
					m_tryThis[i][j][0] = (byte) (m_initialVector[j] ^ m_encrypted[j]);
				}
			}
			m_initialVector = m_blocks[i];
		}
		TryThis();

		// DecryptKey();
		return;
	}

	private void TryThis() {
		int i, j, k;
		for (i = 0; i < m_decrypted.length; i++) {
			for (j = 0; j < 8128; j++) {
				if (m_notFound.indexOf(m_decrypted[i][j][0]) != -1 && m_decrypted[i][j][1] != -1) {
					System.out.print(m_decrypted[i][j][1]);
					for (k = 0; k < 52 - m_countLetters; k++) {
						// if
						// (m_key[m_letterString.indexOf(m_decrypted[i][j][0])][k]
						// < 0) {
						// } else {
						if ((((byte) (m_notUse.charAt(k)) ^ Math
								.abs(m_decrypted[i][j][1])) > 31 && ((byte) (m_notUse.charAt(k)) ^ Math
								.abs(m_decrypted[i][j][1])) < 127)
								|| (((byte) (m_notUse.charAt(k)) ^ Math
										.abs(m_decrypted[i][j][1])) > 9 && ((byte) (m_notUse.charAt(k)) ^ Math
										.abs(m_decrypted[i][j][1])) < 14)) {
							m_key[m_notFound.indexOf(m_decrypted[i][j][0])][k] += 1;
							// System.out.println(key[m_letterString
							// .indexOf(m_decrypted[i][j][0])][k]);
						} else {
							System.out.println(m_key[m_notFound.indexOf(m_decrypted[i][j][0])][k]);
							m_key[m_notFound.indexOf(m_decrypted[i][j][0])][k] = Integer.MIN_VALUE;
							System.out.println(m_key[m_notFound.indexOf(m_decrypted[i][j][0])][k]);
						}
						// }
					}
				}
			}
		}
		for (i = 0; i < 52 - m_countLetters; i++) {
			for (j = 0; j < 52 - m_countLetters; j++) {
				if (m_key[i][j] > 0) {
					System.out.print((char) (m_letterArray[j]) + ":" + " "
							+ (char) (m_letterArray[i]) + " ");
					if (m_keyArray[j][1] == ' ')
						m_keyArray[j][1] = (char) (m_letterArray[j]);
				}
				// System.out.print((char)('a' + j) +":" +" " + (char)('a' +
				// i)+" ");
			}
			System.out.println();
		}
	}

	private void DecryptKey() throws IOException {
		int i, j;
		int max = 0;
		int place = -1;
		int[] temp = m_count.clone();
		for (j = 0; j < temp.length; j++) {
			for (i = 0; i < temp.length; i++) {
				if (temp[i] > max) {
					place = i;
					max = m_count[i];
				}
			}
			max = 0;
			m_keyArray[m_order[j] - 'a'][1] = (char) (place + 'a');
			temp[place] = 0;
			place = 0;
		}
		DecryptFile();
	}

	private boolean CheckDecrypt(int i, int j, int place) {
		if (place < j) {
			for (; place < j; place++) {
				if (m_decrypted[i][place][1] != -1)
					return true;
			}
		} else {
			for (; place < 10; place++)
				if (m_decrypted[i - 1][place][1] != -1)
					return true;
			for (place = 0; place < j; place++) {
				if (m_decrypted[i][place][1] != -1)
					return true;
			}

		}
		return false;
	}

	private void DecryptFile() throws IOException {
		int i, j;
		int place = -1;
		int index = 0;
		// byte[] temp = new byte[10];
		// for (i = 0; i < m_tryThis.length; i++) {
		// for (j = 0; j < 10; j++) {
		// if (m_decrypted[i][j][1] == -1 && m_decrypted[i][j][0] == ' ') {
		// if (place != -1)
		// if (CheckDecrypt(i, j, place))
		// if (j > place)
		// Compare(j - place - 1, place, i);
		// if (place > j)
		// CompareTwo(10 - place + j, place, i, i - index);
		// place = j + 1;
		// index = i;
		//
		// }
		// if (m_tryThis[i][j][1] != -1 && m_tryThis[i][j][1] != 0) {
		// m_tryThis[i][j][0] = (byte) m_keyArray[m_decrypted[i][j][0] -
		// 'a'][1];
		// m_tryThis[i][j][0] = (byte) (m_decrypted[i][j][1] ^
		// m_decrypted[i][j][0]);
		// m_tryThis[i][j][1] = -1;
		// }
		// temp[j] = m_tryThis[i][j][0];
		// }
		// FileWriter file = new FileWriter("file3.txt", true);
		// file.write(new String(temp));
		// file.close();
		// }
	}

	private void EncryptionKey(String key) {
		int i;
		//Random random = new Random();
		Boolean check=false;
		char pos=' ';
		Boolean position= true;
		for (i = 0; i <key.length(); i++) {
				if(key.charAt(i)==' ' || key.charAt(i)=='\n')
					continue;
				else
				{
					if(check==false)
					{
						
						pos=key.charAt(i);
						check =true;
						if(((int)pos>64&&(int)pos<91))
								position=false;
					}
					else
					{
						check=false;
						if(position==false)
							m_keyArray[(int)(pos-65+26)][1] =key.charAt(i);
						else
							m_keyArray[pos-'a'][1] =key.charAt(i);
						position=true;
					}
				}
		}
	}
//		int i;
//		Random random = new Random();
//		for (i = 0; i < 52; i++) {
//			int create;
//			do {
//				create = random.nextInt(52);// draws a number
//			} while (NotFoundTwoDimensionArray(i, create));
//			// check if we used the letter or it is the same letter
//			m_keyArray[i][1] = m_letterArray[create];// found new letter to put
//														// in the encryption key
//		}
//	}

	private void MoveToBlocks(byte[] text) {
		int i, j;
		for (i = 0; i < m_blocks.length; i++) {
			for (j = 0; j < 8128; j++) {
				if (text.length > i * 8128 + j)// check if length of the array
												// is
												// bigger or it needs to put
												// zero
					m_blocks[i][j] = text[i * 8128 + j];// puts the value inside
														// of it
				else
					m_blocks[i][j] = 0;// if bigger puts zero
			}
		}
	}

	private boolean NotFoundTwoDimensionArray(int index, int random) {
		if (index == 0)// if first it is ok for sure
			return false;
		int i;
		for (i = 0; i < index; i++) {// check if in the array we used the letter
										// already
			if (m_keyArray[i][1] == m_letterArray[random])
				return true;
		}
		return false;
	}

	public char[] GetKey() {
		int i;
		char[] res = new char[52];
		for (i = 0; i < 52; i++)
			res[i] = m_keyArray[i][1];
		return res;
	}

	public static void main(String[] args) throws IOException {
		int i;
		String start = args[0];
		if (start.equals("DecryptionB")) {
			File first = new File(args[1]);
			File second = new File(args[2]);
			File third = new File(args[3]);
			File fourth = new File(args[4]);
			int initialVectorSize = 8128;

			byte[] text = Files.readAllBytes(third.toPath());// reads the file
																// to
																// bytes

			byte[] initialVector = Files.readAllBytes(fourth.toPath());
			byte[] knownText = Files.readAllBytes(first.toPath());
			byte[] chiperKnow = Files.readAllBytes(second.toPath());
			PlainTextAttack toEncrypt = new PlainTextAttack(text,
					initialVector, knownText, chiperKnow, initialVectorSize);
			toEncrypt.Decrypt();
			char[] res = toEncrypt.GetKey();
			// FileWriter writer = new FileWriter("myKey.txt");
			for (i = 0; i < res.length; i++) {
				System.out.println(m_letterString.charAt(i) + " " + res[i]
						+ '\n');
			}
			// writer.close();
		}
		if (start.equals("EncryptionB")) {
			File first = new File(args[1]);
			File second = new File(args[2]);
			File third = new File(args[3]);
			byte[] key = Files.readAllBytes(second.toPath());
			int initialVectorSize = 8128;
			byte[] text = Files.readAllBytes(first.toPath());// reads the file
																// to
																// bytes

			byte[] initialVector = Files.readAllBytes(third.toPath());
			PlainTextAttack toEncrypt = new PlainTextAttack(text, key,
					initialVector, initialVectorSize);
			String Skey=new String(key);
			toEncrypt.Encrypt(Skey);
			char[] res = toEncrypt.GetKey();
			FileWriter writer = new FileWriter("myKey.txt");
			for (i = 0; i < res.length; i++) {
				writer.write(m_letterString.charAt(i) + " " + res[i] + '\n');
			}
			writer.close();
		}
	}

	public void PrintArray(char[][] array) {
		int i;
		for (i = 0; i < array.length; i++) {
			System.out.println(array[i][0]);
		}
	}
}
